<template>
  <div class="demo">
    <div
      ref="d3demoRef"
      class="demo-item"
      :style="{
        width: width + 'px'
      }"
    ></div>
    <div
      ref="d3demo2Ref"
      class="demo-item"
      :style="{
        width: width + 'px'
      }"
    ></div>
  </div>
</template>

<script setup lang="ts">
import { onDeactivated, onMounted, ref } from 'vue'
import * as d3 from 'd3'
import { demoData1 } from './data'
import { CommonUtil } from 'hippo-module-core'

interface UserType {
  name: string
  age: number
}

const d3demoRef = ref()
const d3demo2Ref = ref()
const tooltip = ref<any>(null)
const tooltipId = 'd3-tooltip' + CommonUtil.UUID16()

const width = 550
const height = 500
const margin = { top: 10, bottom: 10, left: 10, right: 10 }
const barValueWidth = 160
const barRanking = 30
const barRankingRadius = 10
const barWidth = width - margin.left - margin.right - barValueWidth - barRanking
const barHeight = 20
const barStep = 10
const barRadius = 10

const colorScale = d3.scaleOrdinal().domain(d3.range(demoData1.length)).range(d3.schemeCategory10)

const init = () => {
  d3.select(d3demoRef.value)
    .append('svg')
    .attr('height', height - 10)
    .attr('width', width - 10)
  const svg = d3.select(d3demoRef.value).select('svg')
  const inner = svg
    .append('g')
    .attr('id', 'innerContainer')
    .attr('height', height - margin.top - margin.bottom)
    .attr('width', width - margin.left - margin.right)
    .attr('transform', `translate(${margin.left},${margin.top})`)

  //排行板背景条
  inner
    .append('g')
    .attr('id', 'rectBgContainer')
    .selectAll('rect')
    .data(demoData1.sort((a, b) => d3.descending(a.age, b.age)))
    .join('rect')
    .attr('stroke', (d: UserType, i: number) => colorScale(i))
    .attr('stroke-width', 1.5)
    .attr('fill', (d: UserType, i: number) => colorScale(i))
    .attr('fill-opacity', 0.25)
    .attr('width', barWidth)
    .attr('height', barHeight)
    .attr('ry', barRadius)
    .attr('x', barRanking)
    .attr('y', (d: UserType, i: number) => {
      return i * barHeight + i * barStep
    })

  //排行板柱体
  inner
    .append('g')
    .attr('id', 'rectContainer')
    .selectAll('rect')
    .data(demoData1.sort((a, b) => d3.descending(a.age, b.age)))
    .join('rect')
    .attr('fill', (d: UserType, i: number) => {
      const id = createLinearGradient(svg, 'rect-linear', colorScale(i), colorScale(i), 0.15, 1)
      return `url(#${id})`
    })
    .attr('width', (d: UserType, i: number) => {
      return (d.age * barWidth) / 100 - 6
    })
    .attr('height', barHeight - 6)
    .attr('x', barRanking + 3)
    .attr('y', (d: UserType, i: number) => {
      return i * barHeight + i * barStep + 3
    })
    .attr('ry', barRadius - 3)
    .attr('cursor', 'pointer')
    .on('mouseover', (d: any, data: UserType) => {
      const ele = d3.select(d.srcElement)
      ele.transition().duration(300).attr('opacity', '0.65')
      tooltipVisible(d, data)
    })
    .on('mouseout', (d: any, data: UserType) => {
      const ele = d3.select(d.srcElement)
      ele.transition().duration(300).attr('opacity', '1')
      tooltipHidden()
    })

  // 柱状图文本信息

  inner.append('g').attr('id', 'rankingContainer')

  inner
    .select('#rankingContainer')
    .selectAll('circle')
    .data(demoData1)
    .join('circle')
    .attr('cy', (d: UserType, i: number) => {
      return i * barHeight + i * barStep + barHeight / 2
    })
    .attr('cx', barRankingRadius)
    .attr('r', barRankingRadius)
    .attr('stroke', (d: UserType, i: number) => {
      if (i === 0) {
        return '#ff562e'
      }
      if (i === 1) {
        return '#ff900e'
      }
      if (i === 2) {
        return '#0596da'
      }
      return '#0bbd63'
    })
    .attr('fill', (d: UserType, i: number) => {
      if (i === 0) {
        return '#ff562e'
      }
      if (i === 1) {
        return '#ff900e'
      }
      if (i === 2) {
        return '#0596da'
      }
      return '#0bbd63'
    })
    .attr('fill-opacity', 0.25)

  inner
    .select('#rankingContainer')
    .selectAll('label')
    .data(demoData1)
    .join('text')
    .attr('fill', (d: UserType, i: number) => {
      if (i === 0) {
        return '#ff562e'
      }
      if (i === 1) {
        return '#ff900e'
      }
      if (i === 2) {
        return '#0596da'
      }
      return '#0bbd63'
    })
    .attr('font-size', '0.85em')
    .attr('y', (d: UserType, i: number) => {
      return i * barHeight + i * barStep + barHeight / 2 + 5
    })
    .attr('x', (d: UserType, i: number) => {
      if (i < 9) {
        return 6
      } else {
        return 3
      }
    })
    .text((d: UserType, i: number) => i + 1)

  // 柱状图值信息
  inner
    .append('g')
    .attr('id', 'valueContainer')
    .selectAll('label')
    .data(demoData1)
    .join('text')
    .attr('fill', '#000')
    .attr('font-size', '0.85em')
    .attr('y', (d: UserType, i: number) => {
      return i * barHeight + i * barStep + barHeight / 2 + 5
    })
    .attr('x', barRanking + barWidth + 10)
    .text((d: UserType, i: number) => d.name + '：' + d.age + '岁')
}

const init2 = () => {
  d3.select(d3demo2Ref.value)
    .append('svg')
    .attr('height', height - 10)
    .attr('width', width - 10)
  const svg = d3.select(d3demo2Ref.value).select('svg')
  const inner = svg
    .append('g')
    .attr('id', 'innerContainer')
    .attr('height', height - margin.top - margin.bottom)
    .attr('width', width - margin.left - margin.right)
    .attr('transform', `translate(${margin.left},${margin.top})`)

  //排行板背景条
  inner
    .append('g')
    .attr('id', 'rectBgContainer')
    .selectAll('rect')
    .data(demoData1.sort((a, b) => d3.descending(a.age, b.age)))
    .join('rect')
    //.attr('stroke', (d: UserType, i: number) => colorScale(i))
    //.attr('stroke-width', 1.5)
    .attr('fill', (d: UserType, i: number) => colorScale(i))
    .attr('fill-opacity', 0.25)
    .attr('width', barWidth)
    .attr('height', barHeight)
    .attr('x', barRanking)
    .attr('y', (d: UserType, i: number) => {
      return i * barHeight + i * barStep
    })

  //排行板柱体
  inner
    .append('g')
    .attr('id', 'rectContainer')
    .selectAll('rect')
    .data(demoData1.sort((a, b) => d3.descending(a.age, b.age)))
    .join('rect')
    .attr('fill', (d: UserType, i: number) => {
      return colorScale(i)
    })
    .attr('width', (d: UserType, i: number) => {
      return (d.age * barWidth) / 100
    })
    .attr('height', barHeight)
    .attr('x', barRanking)
    .attr('y', (d: UserType, i: number) => {
      return i * barHeight + i * barStep
    })
    .attr('cursor', 'pointer')
    .on('mouseover', (d: any, data: UserType) => {
      const ele = d3.select(d.srcElement)
      ele.transition().duration(300).attr('opacity', '0.65')
      tooltipVisible(d, data)
    })
    .on('mouseout', (d: any, data: UserType) => {
      const ele = d3.select(d.srcElement)
      ele.transition().duration(300).attr('opacity', '1')
      tooltipHidden()
    })

  // 柱状图文本信息

  inner.append('g').attr('id', 'rankingContainer')

  inner
    .select('#rankingContainer')
    .selectAll('rect')
    .data(demoData1)
    .join('rect')
    .attr('y', (d: UserType, i: number) => {
      return i * barHeight + i * barStep
    })
    .attr('x', 0)
    .attr('width', barHeight)
    .attr('height', barHeight)
    .attr('stroke-width', 1)
    .attr('stroke', (d: UserType, i: number) => {
      if (i === 0) {
        return '#ff562e'
      }
      if (i === 1) {
        return '#ff900e'
      }
      if (i === 2) {
        return '#0596da'
      }
      return '#0bbd63'
    })
    .attr('fill', (d: UserType, i: number) => {
      if (i === 0) {
        return '#ff562e'
      }
      if (i === 1) {
        return '#ff900e'
      }
      if (i === 2) {
        return '#0596da'
      }
      return '#0bbd63'
    })
    .attr('fill-opacity', 0.25)

  inner
    .select('#rankingContainer')
    .selectAll('label')
    .data(demoData1)
    .join('text')
    .attr('fill', (d: UserType, i: number) => {
      if (i === 0) {
        return '#ff562e'
      }
      if (i === 1) {
        return '#ff900e'
      }
      if (i === 2) {
        return '#0596da'
      }
      return '#0bbd63'
    })
    .attr('font-size', '0.85em')
    .attr('y', (d: UserType, i: number) => {
      return i * barHeight + i * barStep + barHeight / 2 + 5
    })
    .attr('x', (d: UserType, i: number) => {
      if (i < 9) {
        return 6
      } else {
        return 3
      }
    })
    .text((d: UserType, i: number) => i + 1)

  // 柱状图值信息
  inner
    .append('g')
    .attr('id', 'valueContainer')
    .selectAll('label')
    .data(demoData1)
    .join('text')
    .attr('fill', '#000')
    .attr('font-size', '0.85em')
    .attr('y', (d: UserType, i: number) => {
      return i * barHeight + i * barStep + barHeight / 2 + 5
    })
    .attr('x', barRanking + barWidth + 10)
    .text((d: UserType, i: number) => d.name + '：' + d.age + '岁')
}

const createLinearGradient = (
  svg: any,
  id: string,
  startColor: string,
  endColor: string,
  startOpacity: number,
  endOpacity: number
) => {
  const linearGradientId = id + CommonUtil.shortUUID()
  const linearGradient = svg
    .append('defs')
    .append('linearGradient')
    .attr('id', linearGradientId)
    .attr('x1', '0%')
    .attr('x2', '100%')
    .attr('y1', '0%')
    .attr('y2', '0%')
  linearGradient
    .append('stop')
    .attr('offset', '0%')
    .style('stop-color', startColor)
    .style('stop-opacity', startOpacity)
  linearGradient
    .append('stop')
    .attr('offset', '100%')
    .style('stop-color', endColor)
    .style('stop-opacity', endOpacity)

  return linearGradientId
}

const tooltipHidden = () => {
  tooltip.value.transition().duration(300).style('opacity', 0).style('display', 'none')
}

const tooltipVisible = (event: any, data: UserType) => {
  tooltip.value.transition().duration(300).style('opacity', 1).style('display', 'block')
  tooltip.value
    .html(
      `<div>
         ${data.name} : ${data.age} 岁
      </div>`
    )
    .style('left', event.pageX + 2 + 'px')
    .style('top', event.pageY + 2 + 'px')
}

const createTooltip = () => {
  tooltip.value = d3
    .select('body')
    .append('div')
    .attr('id', tooltipId)
    .style('opacity', 0)
    .style('position', 'fixed')
    .style('font-size', '12px')
    .style('text-align', 'center')
    .style('background-color', 'rgba(255,255,255,0.95)')
    .style('border-radius', '3px')
    .style('box-shadow', '0px 0px 5px rgba(10, 10, 10,0.2)')
    .style('padding', '8px')
    .style('z-index', '998')
    .style('cursor', 'pointer')
    .style('display', 'none')
}

const removeTooltip = () => {
  const tooltip = d3.select('body').select('#' + tooltipId)
  if (tooltip) {
    tooltip.remove()
  }
}

onDeactivated(() => {
  removeTooltip()
})

onMounted(() => {
  init()
  init2()
  createTooltip()
})
</script>

<style lang="scss" scoped>
.demo {
  width: 100%;
  height: 100%;
  display: flex;
  &-item {
    overflow-x: auto;
    overflow-y: auto;
    height: 500px;
  }
}
</style>
